package cn.luowb.checkchat.common.util;

import cn.luowb.checkchat.common.context.UserInfoDTO;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;

/**
 * Token 工具类（与 UserConfiguration 中的拦截器保持一致）
 * - Redis 使用 StringRedisTemplate
 * - Key: redisKeyUtil.buildUserLoginToken(token)
 * - Value: UserInfoDTO 的 JSON 字符串
 * - TTL: 30 分钟（读取时滑动续期）
 */
@Component
@RequiredArgsConstructor
public class TokenUtils {

    private final StringRedisTemplate stringRedisTemplate;
    private final RedisKeyUtil redisKeyUtil;

    /** 与拦截器一致：20 分钟内续期至 30 分钟，这里直接统一设置为 30 分钟 */
    private static final Duration TOKEN_TTL = Duration.ofMinutes(30);

    /**
     * 生成 Token 并写入 Redis（仅有 uid 时）
     * @param uid 用户ID
     */
    public String generateToken(Long uid) {
        UserInfoDTO info = UserInfoDTO.builder().userId(uid).build();
        return generateToken(info);
    }

    /**
     * 生成 Token 并写入 Redis（包含完整的用户信息）
     * @param info 用户信息（仅 userId 必填，其余可选）
     */
    public String generateToken(UserInfoDTO info) {
        String token = cn.hutool.core.lang.UUID.fastUUID().toString(true);
        // 存 JSON（不必把 token 存进值里，拦截器会在读取后回填到 DTO）
        String key = redisKeyUtil.buildUserLoginToken(token);
        stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(info), TOKEN_TTL);
        return token;
    }

    /**
     * 从请求头 Authorization: Bearer <token> 解析 userId（命中后滑动续期）
     * @return 用户ID
     */
    public Long getUidFromToken() {
        HttpServletRequest request = currentRequest();
        String authorization = request.getHeader("Authorization");
        if (authorization == null || !authorization.startsWith("Bearer ")) {
            throw new RuntimeException("Token 格式错误，应为 Bearer <token>");
        }
        String token = authorization.substring(7).trim();

        String key = redisKeyUtil.buildUserLoginToken(token);
        String json = stringRedisTemplate.opsForValue().get(key);
        if (json == null) {
            throw new RuntimeException("Token 已过期或无效");
        }

        UserInfoDTO info = JSON.parseObject(json, UserInfoDTO.class);
        if (info == null || info.getUserId() == null) {
            throw new RuntimeException("Token 解析失败");
        }

        // 滑动续期至 30 分钟（与拦截器一致）
        stringRedisTemplate.expire(key, TOKEN_TTL);
        return info.getUserId();
    }

    /**
     * 注销 Token（登出）
     */
    public void invalidateToken() {
        HttpServletRequest request = currentRequest();
        String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.startsWith("Bearer ")) {
            String token = authorization.substring(7).trim();
            stringRedisTemplate.delete(redisKeyUtil.buildUserLoginToken(token));
        }
    }

    /* ============== 工具方法 ============== */

    private HttpServletRequest currentRequest() {
        ServletRequestAttributes attrs =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs == null) throw new IllegalStateException("当前线程无请求上下文");
        return attrs.getRequest();
    }
}
